import unittest
from .testHelpers import get_cpp_function_list_with_extension
from lizard_ext.lizardnd import LizardExtension as NestDepth


def get_cpp_with_nestdepth(source):
    return get_cpp_function_list_with_extension(source, NestDepth())


class TestCppNestingDepth(unittest.TestCase):

    def test_one_function_with_no_nd_condition(self):
        result = get_cpp_with_nestdepth("int fun(){}")
        self.assertEqual(0, result[0].max_nesting_depth)

    def test_one_function_with_one_nd_condition(self):
        result = get_cpp_with_nestdepth("int fun(){if(a){xx;}}")
        self.assertEqual(1, result[0].max_nesting_depth)

    def test_one_function_with_one_nd_condition_no_curly_bracket(self):
        result = get_cpp_with_nestdepth("int fun(){if(a) xx;}")
        self.assertEqual(1, result[0].max_nesting_depth)

    def test_one_function_nd_with_else(self):
        result = get_cpp_with_nestdepth("int fun(){if(a)xx;else b;}")
        self.assertEqual(1, result[0].max_nesting_depth)

    def test_one_function_nd_with_else_and_curly_bracket(self):
        result = get_cpp_with_nestdepth("int fun(){if(a){xx;}else b;}")
        self.assertEqual(1, result[0].max_nesting_depth)

    def test_one_function_nd_with_question_mark(self):
        result = get_cpp_with_nestdepth("int fun(){return (a)?b:c;}")
        self.assertEqual(1, result[0].max_nesting_depth)

    def test_one_function_nd_with_forever_loop(self):
        result = get_cpp_with_nestdepth("int fun(){for(;;){dosomething();}}")
        self.assertEqual(1, result[0].max_nesting_depth)

    def test_one_function_nd_with_and(self):
        result = get_cpp_with_nestdepth("int fun(){if(a&&b){xx;}}")
        self.assertEqual(2, result[0].max_nesting_depth)

    def test_one_function_nd_with_else_if(self):
        result = get_cpp_with_nestdepth("int fun(){if(a)b;else if (c) d;}")
        self.assertEqual(2, result[0].max_nesting_depth)

    def test_one_function_nd_with_non_r_value_ref_in_body(self):
        result = get_cpp_with_nestdepth("int f() {a && b==c;}")
        self.assertEqual(1, result[0].max_nesting_depth)

    def test_two_function_nd_with_non_r_value_ref_in_body(self):
        result = get_cpp_with_nestdepth("""
        x c() {
          if (a && b) {
          }
        }
        x a() {
          inputs = c;
        }
        """)
        self.assertEqual(0, result[1].max_nesting_depth)

    def test_one_function_nd_with_nested_loop_statement_plus_curly_brackets(self):
        result = get_cpp_with_nestdepth("""
        x c() {
          if (a && b) {
            if( a != 0 ){
                a = b;
            }
          }
        }
        x a() {
          if (a && b){
            if( a != 0 )
                a = b;
          }
        }
        """)
        self.assertEqual(3, result[0].max_nesting_depth)

    def test_one_function_nd_with_nested_loop_statement_minus_curly_brackets(self):
        result = get_cpp_with_nestdepth("""
        x c() {
          if (a && b) {
            if( a != 0 ){
                a = b;
            }
          }
        }
        x a() {
          if (a && b){
            if( a != 0 )
                a = b;
          }
        }
        """)
        self.assertEqual(3, result[0].max_nesting_depth)

    def test_one_function_nd_with_nested_loop_statement_mixed_curly_brackets(self):
        result = get_cpp_with_nestdepth("""
        x c() {
          if (a && b) {
            if(a != 0){
                a = b;
            }
          }
          if (a && b){
            if(a != 0)
                a = b;
          }
        }
        """)
        self.assertEqual(3, result[0].max_nesting_depth)

    def test_one_function_nd_ignoring_explicit_forever_loop(self):
        result = get_cpp_with_nestdepth("""
        x a() {
          for(;;) {  // <-- explicit forever loop
            if(a != 0){
                a = b;
            }
          }
        }
        x b() {
          while(1) {
            if(a != 0){
                a = b;
            }
          }
        }
        x c() {
          while(true) {
            if(a != 0){
                a = b;
            }
          }
        }
        """)
        self.assertEqual(2, result[0].max_nesting_depth)
        self.assertEqual(2, result[1].max_nesting_depth)
        self.assertEqual(2, result[2].max_nesting_depth)

    def test_one_function_nd_with_addl_statement_in_if(self):
        result = get_cpp_with_nestdepth("""
        int fun() {
            if(a) {
                b;
                if (c) {
                    d;
                }
            }
        }""")
        self.assertEqual(2, result[0].max_nesting_depth)

    def test_nesting_depth_with_multiple_or_operators_bug_426(self):
        """Test for issue #426: multiple || operators should not accumulate nesting depth incorrectly"""
        result = get_cpp_with_nestdepth("""
        void foo(void) {
           // BlockA
           if (a) {           // ND=1
              if (a || b) {   // ND=2 (if + first ||)
                 bar = 1;     // ND=2
              }               // ND=1
           }                  // ND=0

           // BlockB
           if (a || b || c || d || e) {   // ND=2 (if + first ||, not 5!)
              foo = 1;                    // ND=2
           }                              // ND=0

           // BlockC
           if (a || b || c || d || e) { // ND=2 (if + first ||, not 3+5=8!)
              foo = 1;                  // ND=2
              bar = 1;                  // ND=2
              hoo = 2;                  // ND=2
           }                            // ND=0
        }
        """)
        self.assertEqual(3, result[0].max_nesting_depth)

    def test_nesting_depth_with_multiple_and_operators(self):
        """Test that multiple && operators don't accumulate nesting depth incorrectly"""
        result = get_cpp_with_nestdepth("""
        void foo(void) {
           if (a && b && c && d && e) {   // ND=2 (if + first &&, not 5!)
              foo = 1;                    // ND=2
           }                              // ND=0
        }
        """)
        self.assertEqual(2, result[0].max_nesting_depth)

    def test_nesting_depth_with_mixed_and_or_operators(self):
        """Test that mixed && and || operators don't accumulate nesting depth incorrectly"""
        result = get_cpp_with_nestdepth("""
        void foo(void) {
           if (a && b || c && d || e) {   // ND=2 (if + first &&, not 5!)
              foo = 1;                    // ND=2
           }                              // ND=0
        }
        """)
        self.assertEqual(2, result[0].max_nesting_depth)

